package com.jy.datapipeline.export.splitkey.utils;

import com.jy.datapipeline.common.constants.ExpConstants;
import com.jy.datapipeline.common.exception.GlobalException;
import com.jy.datapipeline.export.common.dto.RangeDto;
import com.jy.datapipeline.export.common.enums.impl.ZoomOperationEnums;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

/**
 * @类名 utils
 * @描述 TODO
 * @作者 白条君
 * @创建日期 2023/8/28 11:02
 * @版本 1.0
 */
public class SplitKeyUtil {

    public static BigInteger getPiecesInterval(BigInteger maxSplitPK, BigInteger minSplitPK, BigInteger pieces) {
        BigDecimal bigDecimal = new BigDecimal(maxSplitPK.subtract(minSplitPK));
        bigDecimal = bigDecimal.divide(new BigDecimal(pieces), 0, RoundingMode.HALF_UP);
        return bigDecimal.toBigInteger();
    }

    /**
     * 获取分片数【弃用】
     * @param totalCount
     * @param rowRange
     * @return
     */
    @Deprecated
    public static BigInteger getPieces(BigInteger totalCount, BigInteger rowRange) {
        return halfAdjust(totalCount, rowRange).toBigInteger();
    }

    public static BigInteger getReferenceValue(BigInteger totalCount, BigInteger pieces) {
        return halfAdjust(totalCount, pieces).toBigInteger();
    }

    private static BigDecimal halfAdjust(BigInteger dividend, BigInteger divide) {
        return new BigDecimal(dividend).divide(new BigDecimal(divide), 0, RoundingMode.HALF_UP);
    }

    public static String getDataFileSuffix(int i) {
        return i < 10 ? "000" + (i + 1) : "00" + (i + 1);
    }

    /**
     * 计算误差率
     * @param rangeCount
     * @param referenceValue
     * @param errorRate
     * @return
     */
    public static boolean isAboveErrorRate(BigInteger rangeCount, BigInteger referenceValue, Double errorRate) {
        BigDecimal subtracted = new BigDecimal(rangeCount).divide(new BigDecimal(referenceValue), 2, RoundingMode.HALF_UP);
        BigDecimal subtract = new BigDecimal("1.00");
        //如果被减数大于减数，则返回1或0，否则返回-1
        if (subtracted.compareTo(subtract) >= 0) {
            //如果被减数大于减数，就大-小，再和系数比较
            return subtracted.subtract(subtract).compareTo(BigDecimal.valueOf(errorRate)) > 0;
        } else if (subtracted.compareTo(subtract) < 0) {
            return subtract.subtract(subtracted).compareTo(BigDecimal.valueOf(errorRate)) > 0;
        } else {
            throw new GlobalException(ExpConstants.ERROR_RATE_COMPARE_EXP);
        }
    }

    /**
     * 判断是不是最后一个分片键的取值范围
     * 最小值与间隔之合大于最大值：400 + 100 > 499
     * 最小值与间隔之合小于最大值：(49 - 16) <= 32 + 16 <= 49
     * @param leftMin
     * @param piecesInterval
     * @param max
     * @return
     */
    public static boolean isLastRange(BigInteger leftMin, BigInteger max, BigInteger piecesInterval) {
        if (leftMin.compareTo(max) > 0) {
            return true;
        } else return leftMin.compareTo(max.subtract(piecesInterval)) >= 0 && leftMin.compareTo(max) <= 0;
    }

    /**
     * 据分片健取值范围得到原始分片范围【弃用】
     * @param min
     * @param max
     * @param piecesInterval
     * @return
     */
    @Deprecated
    public static List<RangeDto> getOriginRanges(BigInteger min, BigInteger max, BigInteger piecesInterval) {
        List<RangeDto> rangeDtos = new ArrayList<>();
        BigInteger rightMax = min.add(piecesInterval);
        BigInteger leftMin = min;
        rangeDtos.add(new RangeDto(leftMin, rightMax));
        while (true) {
            //当前leftmin=上一次的rightmax
            //当前的rightmax=当前最小值（rightmax）+间隔值
            leftMin = rightMax;
            rightMax = leftMin.add(piecesInterval);
            rangeDtos.add(new RangeDto(leftMin, rightMax));
            //判断是否是最后一个范围，如果是最后一个分片范围就把当前的rightMax作为下一次的leftMin，总的max给下一次的rightmax
            if (SplitKeyUtil.isLastRange(rightMax, max, piecesInterval)) {
                leftMin = rightMax;
                rightMax = max;
                rangeDtos.add(new RangeDto(leftMin, rightMax));
                break;
            }
        }
        return rangeDtos;
    }

    /**
     * 初始化分片键的取值范围
     * @param min
     * @param piecesInterval
     * @return
     */
    public static RangeDto getInitRange(BigInteger min, BigInteger piecesInterval) {
        return new RangeDto(min, min.add(piecesInterval));
    }

    /**
     * 对当前范围的最大值执行缩放操作
     * @param rightMax
     * @param scaleFactor
     * @return
     */
    public static BigInteger executeZoomOperation(BigInteger rightMax, Double scaleFactor, ZoomOperationEnums zoomOperation) {
        BigInteger value = new BigDecimal(rightMax).multiply(BigDecimal.valueOf(scaleFactor)).toBigInteger();
        int code = zoomOperation.getCode();
        if (code == ZoomOperationEnums.ENLARGE_OPERATION.getCode()) {
            return rightMax.add(value);
        } else if (code == ZoomOperationEnums.REDUCE_OPERATION.getCode()) {
            return rightMax.subtract(value);
        } else {
            throw new GlobalException(ExpConstants.ZOOM_OPERATION_EXP);
        }
    }

    public static boolean getIsFinish(List<RangeDto> ranges, int index) {
        return ranges.size() == index + 1;
    }


    /**
     * 在rangeCount集合中取出离目标值最接近的那个值
     * @param rangeCountList
     * @param target
     * @return
     */
    public static BigInteger getCloseValue(List<BigInteger> rangeCountList, BigInteger target) {
        BigInteger index = target.subtract(rangeCountList.get(0)).abs();
        BigInteger res = rangeCountList.get(0);
        for (BigInteger rangeCount : rangeCountList) {
            BigInteger abs = target.subtract(rangeCount).abs();
            if (abs.compareTo(index) <= 0) {
                index = abs;
                res = rangeCount;
            }
        }
        return res;
    }

    /**
     * 判断基准值是否在rangeCount集合中的连个数之间且大于等于前一个值，小于等于后一个值
     * @param referenceValue
     * @return
     */
    public static boolean isExistsBetweenNumbers(List<BigInteger> rangeCountList, BigInteger referenceValue) {
        boolean left = false;
        boolean right = false;
        for (int i = 0; i < rangeCountList.size(); i++) {
            if (referenceValue.compareTo(rangeCountList.get(i)) >= 0) {
                left = true;
            }
            if (i < (rangeCountList.size() - 1) && referenceValue.compareTo(rangeCountList.get(i + 1)) <= 0) {
                right = true;
            }
            //如果某个数存在于两者之间就立刻退出循环
            if (left && right) {
                break;
            }
        }
        return left && right;
    }
}
